/*
 *  ClientDispatch.c
 *  ClientDispatch
 *
 *  Created by Andrey on 09/10/2008.
 *  Copyright 2008 ИУ7-2003. All rights reserved.
 *
 */

#include <stdlib.h>
#include <string.h>
#include "WorkerThreadPool.h"
#include "ClientDispatch.h"
#include "ClientQueue.h"
#include "ClientList.h"
#include "ClientDispatchLog.h"

#include "../Smtp/Smtp.h"

#define CLIENT_DISPATCH_DEBUG

NTCallbacks* _ntCallbacks;

void OnClientConnected(AsciiStringPtr clientAddress)
{
#ifdef CLIENT_DISPATCH_DEBUG
	Log("Client connected from %s, initializing new CDClientInfo", clientAddress);
#endif
	CDClientInfoPtr clientInfo = (CDClientInfoPtr)calloc(1, sizeof(CDClientInfo));
	
	clientInfo->clientAddress = (AsciiStringPtr)malloc(strlen(clientAddress));
	strncpy(clientInfo->clientAddress, clientAddress, 16);
	// Smtp-specific
	clientInfo->clientData = (BytePtr)calloc(1, sizeof(SmtpEnvelope));
	memset(clientInfo->clientData, 0, sizeof(SmtpEnvelope));
	((SmtpEnvelopePtr)(clientInfo->clientData))->messageContents = AllocateDataBuffer(MAX_MESSAGE_LENGTH);
	((SmtpEnvelopePtr)(clientInfo->clientData))->recipientCount = 0;
	clientInfo->dataToSend = AllocateDataBuffer(MAX_COMMAND_OR_REPLY_LENGTH);
	// FSM initialization
	clientInfo->protocolFsm = (struct SmtpFSM*)calloc(1, sizeof(struct SmtpFSM));
	SmtpFSMInit(clientInfo->protocolFsm, NULL, 0);
	
#ifdef CLIENT_DISPATCH_DEBUG
	Log("Adding new client to list");
#endif
	AddClientToList(clientInfo);
}

void OnClientDisconnected(AsciiStringPtr clientAddress)
{
#ifdef CLIENT_DISPATCH_DEBUG
	Log("Client from %s disconnected", clientAddress);
#endif
	CDCloseConnection(clientAddress);
}

void OnDataReceived(AsciiStringPtr clientAddress, DataBuffer receivedData)
{
#ifdef CLIENT_DISPATCH_DEBUG
	Log("Received data from client at %s", clientAddress);
#endif
	CDClientInfoPtr clientInfo = GetClientInfoByAddress(clientAddress);
#ifdef CLIENT_DISPATCH_DEBUG
	Log("Client info for %s at %p", clientAddress, clientInfo);
	Log("Cloning data buffer");
#endif
	clientInfo->recentlyReceived = CloneDataBuffer(receivedData);
#ifdef	CLIENT_DISPATCH_DEBUG
	Log("Adding client to client queue");
#endif
	AddClientToQueue(clientInfo);
}

void CDInit()
{
	_ntCallbacks = (NTCallbacks*)calloc(1, sizeof(NTCallbacks));
	memset(_ntCallbacks, 0, sizeof(NTCallbacks));
	
	_ntCallbacks->dataReceivedCallback = OnDataReceived;
	_ntCallbacks->clientConnectionCallback = OnClientConnected;
	_ntCallbacks->clientDisconnectionCallback = OnClientDisconnected;
	
	NTInit(_ntCallbacks);
	
	InitClientDispatchLog(LOG_FILENAME);
	InitClientList();
	InitClientQueue();
	InitWorkerThreadPool();
}

void CDRun(int port)
{
	NTAwaitConnection(port);
}

void CDCloseConnection(AsciiStringPtr clientAddress)
{
	CDClientInfoPtr clientToRemove = GetClientInfoByAddress(clientAddress);
	SmtpEnvelopePtr clientEnvelope = (SmtpEnvelopePtr)clientToRemove->clientData;
	
	free(clientToRemove->clientAddress);
	DeallocateDataBuffer(&clientEnvelope->messageContents);
	free(clientToRemove->clientData);
	DeallocateDataBuffer(&clientToRemove->dataToSend);
	free(clientToRemove);
}

void CDShutdown()
{
	PurgeWorkerThreadPool();
	PurgeClientQueue();
	PurgeClientList();
	ShutdownClientDispatchLog();
	
	NTShutdown();
	
	free(_ntCallbacks);
}